
(1) DotPlot()
$ find . | grep "R$" | grep -v "testthat" | grep -v "notes" | grep -v "old/" | xargs grep -n "DotPlot" 2>/dev/null --color=auto
./seurat-4.4.0/R/visualization.R:4266:DotPlot <- function(



#' Dot plot visualization
#'
#' Intuitive way of visualizing how feature expression changes across different identity classes (clusters). |直观可视化不同cluster的基因表达水平
#' The size of the dot encodes the percentage of cells within a class, while the color encodes the AverageExpression level
#' across all cells within a class (blue is high). | 点的大小表示表达的百分数，颜色表示 平均表达水平（蓝色表示高表达）
#'
#' @param object Seurat object | Seurat 对象
#' @param assay Name of assay to use, defaults to the active assay | 使用的试验，默认是活动试验
#' @param features Input vector of features, or named list of feature vectors | 基因列表
#' if feature-grouped panels are desired (replicates the functionality of the old SplitDotPlotGG)

#' @param cols Colors to plot: the name of a palette from
#' \code{RColorBrewer::brewer.pal.info}, a pair of colors defining a gradient,
#' or 3+ colors defining multiple gradients (if split.by is set)
#' 颜色调色盘名字，2个渐变色，或者3个以上来定义多个渐变色(如果设置 split.by )

#' @param col.min Minimum scaled average expression threshold (everything smaller will be set to this) | 列的最小值

#' @param col.max Maximum scaled average expression threshold (everything larger will be set to this) | 列的最大值

#' @param dot.min The fraction of cells at which to draw the smallest dot
#' (default is 0). All cell groups with less than this expressing the given
#' gene will have no dot drawn. | 小于该比例细胞的不再画图(默认是0)

#' @param dot.scale Scale the size of the points, similar to cex | dot.scale 标准化点的大小，和 cex 类似

#' @param idents Identity classes to include in plot (default is all) | idents 给定画图的 Identity ，默认是 all

#' @param group.by Factor to group the cells by | 分组列

#' @param split.by Factor to split the groups by (replicates the functionality
#' of the old SplitDotPlotGG);
#' see \code{\link{FetchData}} for more details | 拆分成小图的列

#' @param cluster.idents Whether to order identities by hierarchical clusters
#' based on given features, default is FALSE | 是否基于给定基因 对cluster聚类，默认F

#' @param scale Determine whether the data is scaled, TRUE for default | 是否对数据做scale，默认T

#' @param scale.by Scale the size of the points by 'size' or by 'radius' | 做scale的方式，size 或者 radius

#' @param scale.min Set lower limit for scaling, use NA for default | 做scale 的最小值
#' @param scale.max Set upper limit for scaling, use NA for default | 做scale 的最大值
#'
#' @return A ggplot object | 返回一个 ggplot 对象
#'
#' #接着是导入的R包或者函数
#' @importFrom grDevices colorRampPalette
#' @importFrom cowplot theme_cowplot
#' @importFrom ggplot2 ggplot aes_string geom_point scale_size scale_radius
#' theme element_blank labs scale_color_identity scale_color_distiller
#' scale_color_gradient guides guide_legend guide_colorbar
#' facet_grid unit
#' @importFrom scattermore geom_scattermore
#' @importFrom stats dist hclust
#' @importFrom RColorBrewer brewer.pal.info
#'
#' @export
#' @concept visualization
#'
#' @aliases SplitDotPlotGG
#' @seealso \code{RColorBrewer::brewer.pal.info}
#'
#' @examples
#' data("pbmc_small")
#' cd_genes <- c("CD247", "CD3E", "CD9")
#' DotPlot(object = pbmc_small, features = cd_genes)
#' pbmc_small[['groups']] <- sample(x = c('g1', 'g2'), size = ncol(x = pbmc_small), replace = TRUE)
#' DotPlot(object = pbmc_small, features = cd_genes, split.by = 'groups')
#'
DotPlot <- function(
  object,
  assay = NULL,
  features,
  cols = c("lightgrey", "blue"),
  col.min = -2.5,
  col.max = 2.5,
  dot.min = 0,
  dot.scale = 6,
  idents = NULL,
  group.by = NULL,
  split.by = NULL,
  cluster.idents = FALSE,
  scale = TRUE,
  scale.by = 'radius',
  scale.min = NA,
  scale.max = NA
) {
  #(A1) 指定 assay，或用默认值
  assay <- assay %||% DefaultAssay(object = object)
  DefaultAssay(object = object) <- assay

  #(A2) split.by 如果不是空，且 cols 不在 brewer.pal.info 中
  # 默认 split.by 是NULL，split.colors=F;
  # 则 is.null(split.by) 是T，加!后是F。 那就一定是 split.colors=F 
  split.colors <- !is.null(x = split.by) && !any(cols %in% rownames(x = brewer.pal.info))

  #(A3) 指定 点大小 计算方法：按面积 or 半径
  scale.func <- switch(
    EXPR = scale.by,
    'size' = scale_size,
    'radius' = scale_radius,
    stop("'scale.by' must be either 'size' or 'radius'")
  )

  #(A4) 基因分组，默认无
  feature.groups <- NULL
  # (B1) 是list，或者 基因组名 有 NA：如果输入 基因数组，则默认跳过
  if (is.list(features) | any(!is.na(names(features)))) {
    feature.groups <- unlist(x = sapply(
      X = 1:length(features), #逐个输入 基因组 下标
      FUN = function(x) {
        # rep("A", each=3) #[1] "A" "A" "A"
        return(rep(x = names(x = features)[x], each = length(features[[x]])))
      }
    ))
    # (B2) 如果有名字含有NA，警告：有些基因组没有名字
    if (any(is.na(x = feature.groups))) {
      warning(
        "Some feature groups are unnamed.",
        call. = FALSE,
        immediate. = TRUE
      )
    }
    # (B3)打开list，获取元素
    features <- unlist(x = features)
    # (B4)给分组加name，得到 named array
    names(x = feature.groups) <- features
  }

  #(A5) 键值对list，key=cluster，value=cell id vector.
  # 拆开后 key 是 cluster + 递增编号; value 是 cell id
  # 如: 
  #                  01                 02                 03                 04 
  #"AAACATACAACCAC-1" "AAACTTGATCCAGA-1" "AAAGAGACGGCATT-1" "AAAGTTTGTAGAGA-1"
  cells <- unlist(x = CellsByIdentities(object = object, idents = idents))

  #(A6) 获取数据：指定基因 features(列名), 指定细胞名(行名)
  data.features <- FetchData(object = object, vars = features, cells = cells)

  #(A7) 添加id列: 就是分类列，默认是 seurat_clusters 列: Idents() 的返回值
  data.features$id <- if (is.null(x = group.by)) {
    # (B1)默认 group.by 为空
    # 返回 named vector: id 列是分类cluster编号
    # AAACATACAACCAC-1 AAACATTGAGCTAC-1 AAACATTGATCAGC-1 AAACCGTGCTTCCG-1 
    #                0                3                2                5 
    Idents(object = object)[cells, drop = TRUE]
  } else {
    # (B2)如果 group.by 有内容
    # 取 group.by 列，返回值类似上文(B1)中的 Idents()，然后按照 cells 排序
    object[[group.by, drop = TRUE]][cells, drop = TRUE]
  }
  # (B3) 如果id列不是factor，转为 factor
  if (!is.factor(x = data.features$id)) {
    data.features$id <- factor(x = data.features$id)
  }
  # (B4) 获取 id 列的水平
  id.levels <- levels(x = data.features$id)
  # (B5) 转为向量
  data.features$id <- as.vector(x = data.features$id)


  #(A8) split.by 默认是空，跳过
  if (!is.null(x = split.by)) {
    #
    splits <- object[[split.by, drop = TRUE]][cells, drop = TRUE]
    if (split.colors) {
      if (length(x = unique(x = splits)) > length(x = cols)) {
        stop("Not enough colors for the number of groups")
      }
      cols <- cols[1:length(x = unique(x = splits))]
      names(x = cols) <- unique(x = splits)
    }
    data.features$id <- paste(data.features$id, splits, sep = '_')
    unique.splits <- unique(x = splits)
    id.levels <- paste0(rep(x = id.levels, each = length(x = unique.splits)), "_", rep(x = unique(x = splits), times = length(x = id.levels)))
  }

  #(A9) 生成绘图所需的数据
  data.plot <- lapply(
    # B1) 对分群的 uniq 值 遍历
    X = unique(x = data.features$id),
    FUN = function(ident) {
      # B2) 取分类为ident的子行；列为1: (n-1)列，就是去掉最后的id列
      data.use <- data.features[data.features$id == ident, 1:(ncol(x = data.features) - 1), drop = FALSE]
      # B3) 按列(基因)求平均值，exp(x)-1 后求 mean; 就保持原始值了，似乎没有再次取 log? 
      avg.exp <- apply(
        X = data.use,
        MARGIN = 2,
        FUN = function(x) {
          return(mean(x = expm1(x = x)))
        }
      )

      # B4) 获取表达的细胞占比: 按列(基因)，统计 大于0的百分比
      pct.exp <- apply(X = data.use, MARGIN = 2, FUN = PercentAbove, threshold = 0)

      # B5) 返回一个 list，分别是 表达的 均值 和 细胞比例
      return(list(avg.exp = avg.exp, pct.exp = pct.exp))
    }
  )
  #(A10) 给返回的list命名：uniq ident
  # cluster1: list(avg, pct)
  # cluster2: list(avg, pct)
  names(x = data.plot) <- unique(x = data.features$id)

  #(A11) 是否根据现有基因对 ident 聚类? 默认是F；我通常设置为T
  # 如果设置T，就对 ident 根据聚类结果排序
  if (cluster.idents) {
    # B1)合并list为矩阵
    mat <- do.call(
      what = rbind,
      args = lapply(X = data.plot, FUN = unlist) #对list解开??
    )
    # B2)按列（基因）scale
    mat <- scale(x = mat)
    # B3)id列的水平 重新排序: 按对mat的行求距离，hclust聚类后，取$order对行名排序
    id.levels <- id.levels[hclust(d = dist(x = mat))$order] #一行十分神奇的代码!!
  }

  #(A12) 遍历每个 ident
  data.plot <- lapply(
    X = names(x = data.plot),
    FUN = function(x) {
      # 取该 ident 对应的list
      data.use <- as.data.frame(x = data.plot[[x]])
      # 新列: 行名，也就是基因
      data.use$features.plot <- rownames(x = data.use)
      # 新列 id: 就是 ident
      data.use$id <- x
      return(data.use)
    }
  )
  # 合并为数据框
  data.plot <- do.call(what = 'rbind', args = data.plot)

  #(A13) 如果非空
  if (!is.null(x = id.levels)) {
    # 转为因子，顺序是现在的 id.levels
    data.plot$id <- factor(x = data.plot$id, levels = id.levels)
  }

  #(A14) 获取分组的个数
  ngroup <- length(x = levels(x = data.plot$id))

  #(A15) 如果分组个数为1
  if (ngroup == 1) {
    # B1)不 scale，给警告
    scale <- FALSE
    warning(
      "Only one identity present, the expression values will be not scaled",
      call. = FALSE,
      immediate. = TRUE
    )
    # B2)如果分组个数 <5，且 scale，警告: 分组较少，可能误导
  } else if (ngroup < 5 & scale) {
    warning(
      "Scaling data with a low number of groups may produce misleading results",
      call. = FALSE,
      immediate. = TRUE
    )
  }

  #(A16) 对基因列做scale
  avg.exp.scaled <- sapply(
    # 对基因名遍历
    X = unique(x = data.plot$features.plot),
    FUN = function(x) {
      # B1) 获取该基因的行，avg 列
      data.use <- data.plot[data.plot$features.plot == x, 'avg.exp']
      # B2) scale 默认是 T
      if (scale) {
        # C1) 对列做scale
        data.use <- scale(x = data.use)
        # C2) 最值修正在[-2.5, 2.5]之间
        data.use <- MinMax(data = data.use, min = col.min, max = col.max)
      } else {
      # B3) 默认不走这里：如果不做scale，则做 log(exp+1)变换
        data.use <- log1p(x = data.use)
      }
      return(data.use)
    }
  )

  #(A17) 转置后 变为 一维向量
  avg.exp.scaled <- as.vector(x = t(x = avg.exp.scaled))

  #(A18) cut() 转变为20个bin
  if (split.colors) {
    # 通常F，不走这里
    avg.exp.scaled <- as.numeric(x = cut(x = avg.exp.scaled, breaks = 20))
    # 如果走这里，相当于把表达量分配到 1-20 这几个数字上，最终图上没有颜色图例，只有细胞百分比图例
  }

  #(A19) 新列：每个基因的平均表达量
  data.plot$avg.exp.scaled <- avg.exp.scaled

  #(A20) 把基因列转为 因子，保持 用户输入的基因顺序
  data.plot$features.plot <- factor(
    x = data.plot$features.plot,
    levels = features
  )

  #(A21) 百分比太小的记作NA
  data.plot$pct.exp[data.plot$pct.exp < dot.min] <- NA

  #(A22) 百分比乘以100
  data.plot$pct.exp <- data.plot$pct.exp * 100

  #(A23) [# 通常F，不走这里]
  if (split.colors) {

    splits.use <- vapply(
      X = as.character(x = data.plot$id),
      FUN = gsub,
      FUN.VALUE = character(length = 1L),
      pattern =  paste0(
        '^((',
        paste(sort(x = levels(x = object), decreasing = TRUE), collapse = '|'),
        ')_)'
      ),
      replacement = '',
      USE.NAMES = FALSE
    )

    data.plot$colors <- mapply(
      FUN = function(color, value) {
        return(colorRampPalette(colors = c('grey', color))(20)[value])
      },
      color = cols[splits.use],
      value = avg.exp.scaled
    )

  }

  #(A24) 默认F，返回 'avg.exp.scaled'
  color.by <- ifelse(test = split.colors, yes = 'colors', no = 'avg.exp.scaled')

  #(A25) 默认NA 跳过
  if (!is.na(x = scale.min)) {
    data.plot[data.plot$pct.exp < scale.min, 'pct.exp'] <- scale.min
  }
  if (!is.na(x = scale.max)) {
    data.plot[data.plot$pct.exp > scale.max, 'pct.exp'] <- scale.max
  }

  #(A26) 默认 无 feature.groups，跳过
  if (!is.null(x = feature.groups)) {
    data.plot$feature.groups <- factor(
      x = feature.groups[data.plot$features.plot],
      levels = unique(x = feature.groups)
    )
  }


  ########################
  # 核心绘图函数，使用点图 geom_point

  #(A27)绘图主函数
  plot <- ggplot(data = data.plot, mapping = aes_string(x = 'features.plot', y = 'id')) + #x=基因，y=cluster分组
    geom_point(mapping = aes_string(size = 'pct.exp', color = color.by)) +  #画点，指定 size=pct，color = color.by，参考 A24，是 avg.exp.scaled
    scale.func(range = c(0, dot.scale), limits = c(scale.min, scale.max)) + #设置点的大小：scale_size/scale_radius
    theme(axis.title.x = element_blank(), axis.title.y = element_blank()) + #主题：设置 x/y 轴 title 为空
    guides(size = guide_legend(title = 'Percent Expressed')) +  #设置图例 size 的标题
    labs( #指定x和y轴标题
      x = 'Features',
      y = ifelse(test = is.null(x = split.by), yes = 'Identity', no = 'Split Identity')
    ) +
    theme_cowplot() #这次使用这个主题了

  #(A28) feature.groups 如果输入是向量，跳过。
  # 如果输入的是list("CD4T"=c(), "CD8T"=c(), "macrophage"=c()) 则走这一步
  if (!is.null(x = feature.groups)) {
    plot <- plot + facet_grid( #分面函数
      facets = ~feature.groups, #对基因组 分面
      scales = "free_x", #对x=基因分组 自由
      space = "free_x", #间隔: 
      switch = "y" #
    ) + theme(
      panel.spacing = unit(x = 1, units = "lines"),
      strip.background = element_blank()
    )
  }

  #(A29) 指定颜色：渐变色
  if (split.colors) {
    plot <- plot + scale_color_identity()
  } else if (length(x = cols) == 1) {
    plot <- plot + scale_color_distiller(palette = cols)
  } else {
    # 默认走这里：第一个low，第二个high
    plot <- plot + scale_color_gradient(low = cols[1], high = cols[2])
  }

  #(A30) 默认走：设置 color 图例标题为 'Average Expression'
  if (!split.colors) {
    plot <- plot + guides(color = guide_colorbar(title = 'Average Expression'))
  }

  return(plot)
}



